package com.jdzy.student.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdzy.common.constants.CommonConstants;
import com.jdzy.common.constants.CompetitionConstants;
import com.jdzy.common.exception.DeleteException;
import com.jdzy.common.exception.InsertNullException;
import com.jdzy.model.common.dtos.PageRequestDto;
import com.jdzy.model.common.vos.PageResult;
import com.jdzy.model.common.vos.Result;
import com.jdzy.model.student.dtos.CompetitionDeleteDto;
import com.jdzy.model.student.dtos.CompetitionDto;
import com.jdzy.model.student.pojos.Competition;
import com.jdzy.model.student.pojos.CompetitionAdvisor;
import com.jdzy.model.student.pojos.CompetitionParticipant;
import com.jdzy.model.student.vos.CompetitionInfoVo;
import com.jdzy.student.mapper.CompetitionAdvisorMapper;
import com.jdzy.student.mapper.CompetitionMapper;
import com.jdzy.student.mapper.CompetitionParticipantMapper;
import com.jdzy.student.service.CompetitionService;
import com.jdzy.utils.common.ParseToListOrStringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class CompetitionServiceImpl extends ServiceImpl<CompetitionMapper, Competition> implements CompetitionService {
    @Autowired
    private CompetitionMapper competitionMapper;

    @Autowired
    private CompetitionAdvisorMapper competitionAdvisorMapper;

    @Autowired
    private CompetitionParticipantMapper competitionParticipantMapper;

    /**
     * 新增竞赛信息
     * @param competitionDto
     * @return
     */
    @Override
    @Transactional
    public Result saveCompetition(CompetitionDto competitionDto) {
        //1、检查参数
        if(competitionDto==null || StringUtils.isBlank(competitionDto.getStuId())){
            return Result.error(CommonConstants.PARAM_REQUIRE);
        }
        //2、进行新增竞赛
        Competition competition=new Competition();
        BeanUtils.copyProperties(competitionDto,competition);
        //插入数据库中
        int insert = competitionMapper.insert(competition);
        //插入为空  业务出现问题  抛出InsertNullException
        if(insert==0){
            return Result.error(CommonConstants.INSERT_FAIL);
        }

        //3、进行新增指导老师信息（competition_advisor表）
        //获取竞赛id
        /*
        与前端进行协调  以字符串传递并以","分割
         */
        competitionDto.setAdvisorNames(ParseToListOrStringUtils.parseToListWithString(competitionDto.getAdvisorNameStr()
                , CompetitionConstants.COMPETITION_ADVISOR_SPILT));

        List<String> advisorNames = competitionDto.getAdvisorNames();

        //获取竞赛id
        Long competitionId = competition.getId();
        for (int i = 0; i < advisorNames.size(); i++) {
            String advisorName=advisorNames.get(i);
            //进行注入信息
            CompetitionAdvisor competitionAdvisor=new CompetitionAdvisor();
            competitionAdvisor.setCompetitionId(competitionId);
            competitionAdvisor.setStuId(competitionDto.getStuId());
            competitionAdvisor.setAdvisorName(advisorName);
            try {
                int insertflag = competitionAdvisorMapper.insert(competitionAdvisor);
                if(insertflag==0) throw new InsertNullException();
            }catch (InsertNullException insertNullException){
                //手动回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                log.error("saveCompetition方法,新增指导老师信息失败");
                return Result.error(CommonConstants.INSERT_FAIL);
            }
        }

        //4、进行新增参赛人信息
        /*
        与前端进行协调  以字符串传递并以","分割
         */
        competitionDto.setParticipantNames(ParseToListOrStringUtils.parseToListWithString(competitionDto.getParticipantNameStr()
        ,CompetitionConstants.COMPETITION_ADVISOR_SPILT));

        List<String> participantNames = competitionDto.getParticipantNames();
        for (String participantName : participantNames) {
            CompetitionParticipant competitionParticipant=new CompetitionParticipant();
            competitionParticipant.setCompetitionId(competitionId);
            competitionParticipant.setStuId(competitionDto.getStuId());
            competitionParticipant.setCompetitionParticipantName(participantName);

            try {
                int insertflag = competitionParticipantMapper.insert(competitionParticipant);
                if(insertflag==0) throw new InsertNullException();
            }catch (InsertNullException insertNullException){
                //手动回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                log.error("saveCompetition方法,新增参赛人信息失败");
                return Result.error(CommonConstants.INSERT_FAIL);
            }
        }

        return Result.success();
    }

    /**
     * 获取竞赛信息
     *
     * @param stuId
     * @param page
     * @param size
     * @param competitionDto
     * @return
     */
    @Override
    public Result getCompetitionInfo(String stuId, Integer page, Integer size, CompetitionDto competitionDto) {
        //1、检查参数
        if(StringUtils.isBlank(stuId)){
            return Result.error(CommonConstants.PARAM_REQUIRE);
        }
        //设置分页
        PageRequestDto pageRequestDto=new PageRequestDto(page,size);
        pageRequestDto.checkParam();

        //分页查询
        IPage iPage=new Page(pageRequestDto.getPage(),pageRequestDto.getSize());

        //2.1、条件查询
        LambdaQueryWrapper<Competition> competitionLambdaQueryWrapper = Wrappers.<Competition>lambdaQuery()
                .eq(Competition::getStuId, stuId)
                //在竞赛时间之后的
                .ge(competitionDto.getCompetitionTime()!=null,Competition::getCompetitionTime,competitionDto.getCompetitionTime())
                .like(StringUtils.isNotBlank(competitionDto.getCompetitionName()),Competition::getCompetitionName,competitionDto.getCompetitionName())
                .eq(competitionDto.getCompetitionType()!=null,Competition::getCompetitionType,competitionDto.getCompetitionType())
                .eq(competitionDto.getAwardLevel()!=null,Competition::getAwardLevel,competitionDto.getAwardLevel());

        //3.1、对参赛人(字符串)进行解析
        List<String> participantNames = ParseToListOrStringUtils.parseToListWithString(competitionDto.getParticipantNameStr()
                , CompetitionConstants.COMPETITION_ADVISOR_SPILT);
        //3.2、根据获取的participantNames去参与表中  获取对应的竞赛id
        List<Long> competitionIdsFromCompetitionParticipant = competitionParticipantMapper
                .getCompetitionIdByParticipantNames(stuId, participantNames);
        /*
        当输入的查找人字符串不为空且没有找到  则直接返回空(注意  这里不能约掉 这里必须进行这个判断  competitionIdsFromCompetitionParticipant
        默认就是不在意这条条件了)
        所以这行非常重要
         */
        if(competitionIdsFromCompetitionParticipant==null || competitionIdsFromCompetitionParticipant.size()==0){
            //返回结果
            PageResult pageResult=new PageResult(pageRequestDto.getPage(),pageRequestDto.getSize(),iPage.getTotal());
            return pageResult;
        }
        //3.3、加入搜索内容中去
        competitionLambdaQueryWrapper.in(competitionIdsFromCompetitionParticipant.size()!=0,Competition::getId,competitionIdsFromCompetitionParticipant);

        //4.1、对指导老师(字符串)进行解析
        List<String> advisorNames = ParseToListOrStringUtils.parseToListWithString(competitionDto.getAdvisorNameStr()
                , CompetitionConstants.COMPETITION_ADVISOR_SPILT);
        //4.2、根据获取的advisorNames去指导老师表中  获取对应的竞赛id
        List<Long> competitionIdsFromCompetitionAdvisor = competitionAdvisorMapper
                .getCompetitionIdByAdvisorNames(stuId, advisorNames);
        /*
        当输入的查找人字符串不为空且没有找到  则直接返回空(注意  这里不能约掉 这里必须进行这个判断  competitionIdsFromCompetitionParticipant
        默认就是不在意这条条件了)
        所以这行非常重要
         */
        if(competitionIdsFromCompetitionAdvisor==null || competitionIdsFromCompetitionAdvisor.size()==0){
            //返回结果
            PageResult pageResult=new PageResult(pageRequestDto.getPage(),pageRequestDto.getSize(),iPage.getTotal());
            return pageResult;
        }
        //4.3、加入搜索内容中去
        competitionLambdaQueryWrapper
                .in(competitionIdsFromCompetitionAdvisor.size()!=0,Competition::getId,competitionIdsFromCompetitionAdvisor);


        //5、进行查询Competition表获取信息
        IPage selectPage = competitionMapper.selectPage(iPage, competitionLambdaQueryWrapper);
        List<Competition> competitions = selectPage.getRecords();
        //无竞赛信息
        if(competitions==null || competitions.isEmpty()){
            return Result.success();
        }
        //6、获取其他表的信息进行封装
        List<CompetitionInfoVo> competitionInfoVos=new ArrayList<>();
        for (Competition competition:competitions) {
            Long competitionId = competition.getId();
            //7、获取对应的指导老师
            List<String> advisorNameList = competitionAdvisorMapper.getAdvisorNameList(competitionId, stuId);
            /*
            与前端进行协调  以字符串传递并以","分割
             */
            String advisorNameStr = ParseToListOrStringUtils
                    .parseToString(advisorNameList, CompetitionConstants.COMPETITION_ADVISOR_SPILT);
            //8、获取参赛人员
            List<String> participantNameList = competitionParticipantMapper.getParticipantNameList(competitionId, stuId);
            /*
            与前端进行协调  以字符串传递并以","分割
             */
            String participantNameStr = ParseToListOrStringUtils
                    .parseToString(participantNameList, CompetitionConstants.COMPETITION_ADVISOR_SPILT);

            //9、进行疯转
            CompetitionInfoVo competitionInfoVo=new CompetitionInfoVo();
            BeanUtils.copyProperties(competition,competitionInfoVo);
            competitionInfoVo.setAdvisorNameStr(advisorNameStr);
            competitionInfoVo.setParticipantNameStr(participantNameStr);
            //加入到总的
            competitionInfoVos.add(competitionInfoVo);
        }
        //返回结果
        PageResult pageResult=new PageResult(pageRequestDto.getPage(),pageRequestDto.getSize(),iPage.getTotal());
        pageResult.setData(competitionInfoVos);
        return pageResult;
    }

    /**
     * 删除竞赛信息
     * @param stuId
     * @param competitionDeleteDto
     * @return
     */
    @Override
    @Transactional
    public Result deleteCompetition(String stuId, CompetitionDeleteDto competitionDeleteDto) {
        //1、检查参数
        if(StringUtils.isBlank(stuId) || competitionDeleteDto==null || competitionDeleteDto.getCompetitionIds().isEmpty()){
            return Result.error(CommonConstants.PARAM_REQUIRE);
        }
        List<Long> competitionIds=competitionDeleteDto.getCompetitionIds();
        synchronized ("deleteCompetition"+CommonConstants.LOCK_INDEX+stuId){
            //2、开始操作Competition表
            //2.1、先查询数据库中是否有这些数据
            List<Competition> competitions = competitionMapper.selectList(Wrappers.<Competition>lambdaQuery()
                    .in(Competition::getId, competitionIds)
                    .eq(Competition::getStuId, stuId));
            if(competitions.size()!=competitionIds.size()){
                //数据库中不存在
                return Result.error(CommonConstants.DATASOURCE_IS_NULL);
            }
            //2.2、删除competition表中数据
            int deleteCompetitionNum = competitionMapper.delete(Wrappers.<Competition>lambdaQuery()
                    .eq(Competition::getStuId, stuId)
                    .in(Competition::getId, competitionIds));
            try {
                if(deleteCompetitionNum!=competitionIds.size()) {
                        //删除失败 回滚事务
                        throw new DeleteException();
                }
            }catch (DeleteException deleteException) {
                //手动回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                log.error("deleteCompetition--->删除competition表中数据失败,参数:{}", competitionIds);
                return Result.error(CommonConstants.DELETE_FAIL);
            }
            //2.3、成功后 再进行删除competition_advisor数据
            competitionAdvisorMapper.delete(Wrappers.<CompetitionAdvisor>lambdaQuery()
                    .in(CompetitionAdvisor::getCompetitionId, competitionIds)
                    .eq(CompetitionAdvisor::getStuId, stuId));

            //2.4、成功后 再进行删除competition_participant数据
            competitionParticipantMapper.delete(Wrappers.<CompetitionParticipant>lambdaQuery()
                    .in(CompetitionParticipant::getCompetitionId,competitionIds)
                    .eq(CompetitionParticipant::getStuId,stuId)
            );
            return Result.success();
        }
    }

    /**
     * 修改竞赛信息表
     * @param competitionDto
     * @return
     */
    @Override
    @Transactional
    public Result updateCompetition(CompetitionDto competitionDto) {
        //1、检查参数
        if(competitionDto==null || competitionDto.getId()==null || StringUtils.isBlank(competitionDto.getStuId())){
            return Result.error(CommonConstants.PARAM_REQUIRE);
        }
        //加锁
        synchronized ("updateCompetition"+CommonConstants.LOCK_INDEX+competitionDto.getId()
                +CommonConstants.LOCK_INDEX+competitionDto.getStuId()){
            //2、查询数据库中有无这条数据
            Competition competition = competitionMapper.selectOne(Wrappers.<Competition>lambdaQuery()
                    .eq(Competition::getId, competitionDto.getId())
                    .eq(Competition::getStuId, competitionDto.getStuId()));
            if(competition==null){
                return Result.error(CommonConstants.DATASOURCE_IS_NULL);
            }
            //进行数据拷贝  修改Competition数据库
            BeanUtils.copyProperties(competitionDto,competition);
            int updateById = competitionMapper.updateById(competition);
            if(updateById==0){
                //修改失败
                return Result.error(CommonConstants.UPDATE_FAIL);
            }

            //3、修改成功  开始准备操作CompetitionAdvior表
            //4.1、先从CompetitionAdvior中获取老旧的名称
            List<String> oldAdvisorNames = competitionAdvisorMapper
                    .getAdvisorNameList(competitionDto.getId(), competitionDto.getStuId());
            // 以防有重名人  用Map进行统计
            Map<String,Integer> oldAdvisorNameMap=new HashMap<>();
            for (String oldAdvisorName : oldAdvisorNames) {
                oldAdvisorNameMap.put(oldAdvisorName,oldAdvisorNameMap
                        .getOrDefault(oldAdvisorName,0)+1);
            }
            //4、2同样将传入的数据进行转换成Map
            /*
            这里和前端商议好了 字符串传递  并商议好 "," 为分割
             */
            competitionDto.setAdvisorNames(ParseToListOrStringUtils.parseToListWithString(competitionDto.getAdvisorNameStr()
                    ,CompetitionConstants.COMPETITION_ADVISOR_SPILT));
            List<String> newAdvisorNames = competitionDto.getAdvisorNames();
            Map<String,Integer> newAdvisorNameMap=new HashMap<>();
            for (String newAdvisorName : newAdvisorNames) {
                newAdvisorNameMap.put(newAdvisorName,newAdvisorNameMap
                        .getOrDefault(newAdvisorName,0)+1);
            }
            //4、3判断两者是否相同
            if(!oldAdvisorNameMap.equals(newAdvisorNameMap)){
                //4.4、不一样   图方便全部删除
                competitionAdvisorMapper.delete(Wrappers.<CompetitionAdvisor>lambdaQuery()
                        .eq(CompetitionAdvisor::getCompetitionId,competitionDto.getId())
                );
                //4.5、开始插入新数据
                for (String newAdvisorName : newAdvisorNames) {
                    CompetitionAdvisor competitionAdvisor=new CompetitionAdvisor();
                    competitionAdvisor.setCompetitionId(competitionDto.getId());
                    competitionAdvisor.setStuId(competitionDto.getStuId());
                    competitionAdvisor.setAdvisorName(newAdvisorName);

                    //插入数据库中
                    competitionAdvisorMapper.insert(competitionAdvisor);
                }
            }

            //5、修改成功  开始准备操作CompetitionParticipant表
            //6.1、先从CompetitionParticipant中获取老旧的名称
            List<String> oldParticipantNames = competitionParticipantMapper
                    .getParticipantNameList(competitionDto.getId(), competitionDto.getStuId());
            // 以防有重名人  用Map进行统计
            Map<String,Integer> oldParticipantNameMap=new HashMap<>();
            for (String oldParticipantName : oldParticipantNames) {
                oldParticipantNameMap.put(oldParticipantName,oldParticipantNameMap
                        .getOrDefault(oldParticipantName,0)+1);
            }
            //6、2同样将传入的数据进行转换成Map
            /*
            这里和前端商议好了 字符串传递  并商议好 "," 为分割
             */
            competitionDto.setParticipantNames(ParseToListOrStringUtils.parseToListWithString(competitionDto.getParticipantNameStr()
                    ,CompetitionConstants.COMPETITION_ADVISOR_SPILT));
            List<String> newParticipantNames = competitionDto.getParticipantNames();
            //6.2.1、转换为Map
            Map<String,Integer> newParticipantNameMap=new HashMap<>();
            for (String newParticipantName : newParticipantNames) {
                newParticipantNameMap.put(newParticipantName,newParticipantNameMap
                        .getOrDefault(newParticipantName,0)+1);
            }
            //6.3、判断是否相等
            if(!oldParticipantNameMap.equals(newParticipantNameMap)){
                //6.4、不一样   图方便全部删除
                competitionParticipantMapper.delete(Wrappers.<CompetitionParticipant>lambdaQuery()
                        .eq(CompetitionParticipant::getCompetitionId,competitionDto.getId())
                );
                //6.5、开始插入新数据
                for (String newParticipantName : newParticipantNames) {
                    CompetitionParticipant competitionParticipant=new CompetitionParticipant();
                    competitionParticipant.setCompetitionId(competitionDto.getId());
                    competitionParticipant.setStuId(competitionDto.getStuId());
                    competitionParticipant.setCompetitionParticipantName(newParticipantName);

                    competitionParticipantMapper.insert(competitionParticipant);
                }
            }
        }
        return Result.success();
    }
}
